﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Adaptive_Clustering
{

    public class Distribution : Estimations
    {

        private int Threshold, NumOfPixelsChanged, k;
        private double[] AverageIntensityPerCluster;
        public Distribution(Byte[,] NewImageArray, Byte[,] ImageClusterIndexArray, int k)
        {
            this.k = k;
            Threshold = 0;
            NumOfPixelsChanged = 0;
            AverageIntensityPerCluster = AverageIntesity(NewImageArray, ImageClusterIndexArray, k);
        }

        private double[] AverageIntesity(Byte[,] NewImageArray, Byte[,] ImageClusterIndexArray, int k)
        {
            UInt64[] counter = new UInt64[k];
            double[] AverageIntensity = new double[k];
            for (int i = 0; i < NewImageArray.GetLength(0); i++)
                for (int j = 0; j < NewImageArray.GetLength(1); j++)
                { //Sum Intensity of each region
                    AverageIntensity[ImageClusterIndexArray[i, j]] += NewImageArray[i, j];
                    counter[ImageClusterIndexArray[i, j]]++;
                }

            for (int x = 0; x < k; x++) //Average the Intensity
                AverageIntensity[x] = AverageIntensity[x] / counter[x];

            return AverageIntensity;
        }

        public override bool IsConverged()
        {
            if (NumOfPixelsChanged < Threshold) return true;
            else return false;
        }

        public Byte[,] distribution(Byte[,] ImageClusterIndexArray, Byte[,] ImageArray, double Sigma, double Beta)
        {
            Byte[,] NewImageClusterIndexArray = new Byte[ImageClusterIndexArray.GetLength(0), ImageClusterIndexArray.GetLength(1)];
            int RegionNumber;
            double Gaussean, SumVc;
            Byte MaxIndex;
            double[] newValue = new double[k];
            DefineThreshold(ImageArray.GetLength(0), ImageArray.GetLength(1));
            for (int x = 0; x < ImageArray.GetLength(0); x++)
                for (int y = 0; y < ImageArray.GetLength(1); y++)
                {
                    SumVc = -1 * SumVclique(ImageClusterIndexArray, Beta, x, y);
                    for (RegionNumber = 0; RegionNumber < k; RegionNumber++)
                    {
                        Gaussean = -1 * WhiteGausseanProccess(AverageIntensityPerCluster, ImageArray[x, y], RegionNumber, Sigma);
                        double temp = Math.Exp(Gaussean + SumVc);
                        newValue[RegionNumber] = temp;
                    }
                    MaxIndex = FindMax(newValue, k);
                    CheckForChanges(ImageClusterIndexArray[x, y], MaxIndex);
                    NewImageClusterIndexArray[x, y] = MaxIndex;
                }
            return ImageClusterIndexArray;
        }

        private Byte FindMax(double[] newValue, int k) //Finding max distribution
        {
            double Max = newValue[0];
            Byte MaxIndex = 0;
            for (int i = 1; i < k; i++)
                if (Max < newValue[i])
                {
                    Max = newValue[i];
                    MaxIndex = (Byte)i;
                }
            return MaxIndex;
        }

        private void CheckForChanges(Byte p, Byte newValue)
        {
            if (p == newValue) NumOfPixelsChanged++;
        }

        private void DefineThreshold(int Width, int Height)
        {
            Threshold = Width; // set Threshold to image Width
            if (Width > Height) //if Width>Height
            {
                Threshold = Height; // set Threshold to image Height
            }
            Threshold /= 10;
        }

        private double WhiteGausseanProccess(double[] AverageIntensityPerClster, Byte ImageIntensity, int RegionNumber, double Sigma)
        {
            double Sum = 0;
            //  for (int i = 0; i < NewImageArray.GetLength(0); i++)
            //     for (int j = 0; j < NewImageArray.GetLength(1); j++)
            //      {
            Sum += Math.Pow(((double)ImageIntensity - AverageIntensityPerClster[RegionNumber]), 2) / 2 * Sigma * Sigma;
            //      }
            return Sum;
        }

        private double SumVclique(Byte[,] ImageClusterIndexArray, double Beta, int x, int y)
        {
            int xEnd = x + 1;
            int yEnd = y + 1;
            int i = x - 1;
            int j = y - 1;
            double Sum = 0;
            if (!FarFromBorders(x, ImageClusterIndexArray.GetLength(0), y, ImageClusterIndexArray.GetLength(1)))
            { // Fixing borders (edges)
                if (i < 0) i = 0;
                if (j < 0) j = 0;
                if (xEnd >= ImageClusterIndexArray.GetLength(0)) xEnd = ImageClusterIndexArray.GetLength(0) - 1;
                if (yEnd >= ImageClusterIndexArray.GetLength(1)) yEnd = ImageClusterIndexArray.GetLength(1) - 1;
            }
            // According to formula:
            for (; i < xEnd; i++)
                for (; j < yEnd; j++)
                    if (ImageClusterIndexArray[i, j] == ImageClusterIndexArray[x, y])
                        Sum -= Beta;
                    else
                        Sum += Beta;
            return Sum;
        }
    }
}
